/* Main loop for NTP server */

/* Copyright (C) 2009-2011 David Zanetti
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */


#include <avr/io.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include <string.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

#include "global.h"
#include <util/delay.h>
#include <stdlib.h>

#include "serial.h"
#include "ether.h"
#include "clock.h"
#include "venus634.h"
#include "console.h"
#include "encx24j600.h"
#include "ip.h"
#include "at25df.h"
#include "led.h"
#include "dhcpc.h"
#include "arp.h"
#include "udp.h"

/* this is the signature for a valid eeprom image */
uint16_t EEMEM eepromsig = 0xd027; 

#define MAX_PACKET 590

uint8_t rx_pkt[MAX_PACKET], tx_pkt[MAX_PACKET];

ISR(PCINT1_vect) {
	if (PINB & (1 << PINB0)) {
		if (!(flag_gps & FLAG_GPS_PPS)) {
			flag_gps |= FLAG_GPS_PPS;
			clock_us_pps = TCNT1;
			clock_ms_pps = clock_frac;
			clock_s_gps = clock_s;
		}
	}
	if (!(PINB & (1 << PINB1))) {
		flag_ether |= FLAG_ETHER_INT;
	}
}


/* check the validity of the EEPROM */
void check_eeprom(void) {
	if (eeprom_read_word(&eepromsig) != EEPROM_SIG) {
		/* not correct, nuke things back to defaults */
		ip_eeprom_init();
		console_eeprom_init();
		encx24j600_eeprom_init();
		eeprom_write_word(&eepromsig,EEPROM_SIG);
		serial0_tx_PGM(PSTR("eeprom: setting defaults\r\n"));
	}
}

int main(void) {
	//  int16_t ms_ave[128]; /* last 16 samples from the average */
	//  uint8_t ave_ptr; /* the pointer into the current average sample */

	/* before anything else, init the clock */
	cli(); /* disabl all interrupts, we will enable them later */

	CLKPR = (1 << CLKPCE); /* enable changing clock */
	CLKPR = 0; /* set to 1:1 clock */

	/* STALL */

	/* wipe flags */
	flag_stat = 0;
	flag_clock = 0;
	flag_gps = 0;
	flag_ether = 0;
	flag_ip = 0;
	flag_serial = 0;

	/* we are good to enable interrupts globally. they still need to be
     enabled per-subsystem anyway */
	sei();

	/* init console */
	console_init();

	/* check eeprom */
	check_eeprom(); /* does not return on invalid EEPROM sig */

	/* okay, safe to init the hostname from eeprom now */
	hostname_init();

	/* reset counters */
	global_counters.uptime = 0;
	clear_counters(); /* does not include uptime */

	/* indicate no lock, no alignment */
	led_init();
	LED_RED;

	clock_init();
	// venus634_init();
	//gps_init();

	// enable interrupts on Port B
	PCMSK1 |= (1 << PCINT8); /* PPS */
	PCMSK1 |= (1 << PCINT9); /* EINT */
	PCICR |= (1 << PCIE1); /* enable pin-change interrupts */

	//  i2c_init(); /* needed by enc28j60 */

	// Disabled, current boards have problems with it

	//  enc28j60_init(); /* this populates the eth_mac global too */

	/* initalise the ethernet chip */
	encx24j600_init();

	ip_init(); /* inisalise IP stack defaults */

	if (flag_ip & FLAG_IP_DHCP) {
#ifdef DHCPC_H
		dhcpc_init();
#endif
	}

	/* initalise and identify SPI flash chip attached */
	at25df_init();

	/* okay, we're ready to respond to interrupts from the ethernet chip */
	//  PORTA |= (1 << PORTA7); /* enable weak pull-up of PORTA0 */
	//  PCICR |= (1 << PCIE0); /* enable PCINT0..7 */
	//  PCMSK0 |= (1 << PCINT0); /* enable PCINT0 only */

	/* initalise the moving average store */
	//  for (ave_ptr = 0; ave_ptr < 128; ave_ptr++) {
	//   ms_ave[ave_ptr] = 0;
	//  }
	//  ave_ptr = 0; /* start at .. start */

	/* display the hostname */
	serial0_tx(hostname, hostname_len);
	serial0_tx_PGM(PSTR("# "));

	DDRD |= (1 << PORTD4) | (1 << PORTD5);
	PORTD &= ~(1 << PORTD5);
	PORTD &= ~(1 << PORTD4);

	while(1) {

		if (flag_gps & FLAG_GPS_PPS) {
			clear_flag(flag_gps,FLAG_GPS_PPS);
		}

		console_process();

		if (flag_serial & FLAG_SERIAL_U1_RX) {
			serial0_tx_cout(serial1_rx());
			//    venus634_process();
		}

		if (flag_gps & FLAG_GPS_EVENT_TIME) {
			clear_flag(flag_gps,FLAG_GPS_EVENT_TIME);
			clock_update_s = clock_s_gps;
			clock_align();
		}

		/* handle interrupts from the Ethernet chip */
		if (flag_ether & FLAG_ETHER_INT) {
			clear_flag(flag_ether,FLAG_ETHER_INT);
			encx24j600_interrupt();
		}

		/* process any ethernet frames waiting */
		if (flag_ether & FLAG_ETHER_RX) {
			/* check we have waiting packets */
			if (encx24j600_rx_waiting()) {
				ether_process();
			}
		}

		/* deal with 1Hz events */
		if (flag_clock & FLAG_CLOCK_TICK) {
			clear_flag(flag_clock,FLAG_CLOCK_TICK);
			if (flag_ip & FLAG_IP_DHCP) {
#ifdef DHCPC_H
				dhcpc_tick();
#endif
			}
#ifdef ARP_H
			arp_tick();
#endif
#ifdef DEBUG_UDP_SEND
			if (clock_s % 10 == 0 && (flag_ip & FLAG_IP_CONFIG)) {
				uint8_t pkt[32], addr[4] = {76,74,157,29};
				uint8_t n;

				for (n = 0; n < 32; n++) {
					pkt[n] = n;
				}
				udp_send(addr,1024,1024,pkt,32); /* generate some junk */
			}
#endif
		}

	}
}

